// using FNV hash functions to support switch on strings
namespace fnv1a_32 {

constexpr uint32_t val_32_const = 0x811c9dc5U;
constexpr uint32_t prime_32_const = 0x1000193U;

constexpr auto hash(const char *s, const uint32_t h = val_32_const) -> uint32_t {
    return !*s ? h : hash(s + 1, static_cast<uint32_t>(1ULL*(h ^ uint32_t(*s)) * prime_32_const));
}

inline uint32_t hash_nostack(const char *s) {
    uint32_t h = val_32_const;
    while (*s) {
        h = static_cast<uint32_t>(1ULL * (h ^ uint32_t(*s)) * prime_32_const);
        ++s;
    }
    return h;
}

}

namespace fnv1a_64 {

constexpr uint64_t val_64_const = 0xcbf29ce484222325ULL;
constexpr uint64_t prime_64_const = 0x100000001b3ULL;

constexpr auto lo(uint64_t x) -> uint64_t {
    return x & uint32_t(-1);
}

constexpr auto hi(uint64_t x) -> uint64_t {
    return x >> 32;
}

constexpr auto mulu64(uint64_t a, uint64_t b) -> uint64_t {
    return 0 + (lo(a)*lo(b) & uint32_t(-1))
        + (((((hi(lo(a)*lo(b)) + lo(a)*hi(b)) & uint32_t(-1)) + hi(a)*lo(b)) & uint32_t(-1)) << 32);
}

constexpr auto hash(const char *s, uint64_t h = val_64_const) -> uint64_t {
    return !*s ? h : hash(s + 1, mulu64(h ^ *s, prime_64_const));
}


inline uint64_t hash_nostack(const char *s) {
    uint64_t h = val_64_const;
    while (*s) {
        h = mulu64(h ^ *s, prime_64_const);
        ++s;
    }
    return h;
}

}

inline uint32_t h32_(const std::string &n) {
    return fnv1a_32::hash_nostack(n.c_str());
}

inline uint64_t h64_(const std::string &n) {
    return fnv1a_64::hash_nostack(n.c_str());
}

inline constexpr uint64_t operator ""_h32(const char *p, size_t) {
    return fnv1a_32::hash(p);
}

inline constexpr uint64_t operator ""_h64(const char *p, size_t) {
    return fnv1a_64::hash(p);
}


#define DECL_LOAD_FIELD(supercls) typedef supercls super; \
    ::common::Base *loadField(uint64_t field, const std::string &value) override

#define __BEGIN_LOAD_FIELD_NATIVE(n) \
::common::Base *n(uint64_t field, const std::string &value) { \
    { auto *res = super::loadField(field, value); if (res) return res; } \
    switch (field) {

#define BEGIN_LOAD_FIELD(cls) __BEGIN_LOAD_FIELD_NATIVE(cls::loadField)
#define BEGIN_LOAD_FIELD_INLINE(supercls) typedef supercls super; \
    __BEGIN_LOAD_FIELD_NATIVE(loadField)

#define END_LOAD_FIELD } \
    return nullptr; \
}

#define FIELD_INT(n, m) \
    case #n ##_h64: \
        m = static_cast<decltype(m)>(std::stoll(value)); \
        return this

#define FIELD_UINT(n, m) \
    case #n ##_h64: \
        m = static_cast<decltype(m)>(std::stoull(value)); \
        return this

#define FIELD_FLOAT(n, m) \
    case #n ##_h64: \
        m = std::stof(value); \
        return this

#define FIELD_DOUBLE(n, m) \
    case #n ##_h64: \
        m = std::stod(value); \
        return this

#define FIELD_STR(n, m) \
    case #n ##_h64: \
        m = value; \
        return this

#define FIELD_CHAR(n, m) \
    case #n ##_h64: \
        m = value.length() ? value[0] : 0; \
        return this

#define FIELD_INT_VEC(n, m) \
    case #n ##_h64: \
        m.push_back(static_cast<decltype(m)::value_type>(std::stoll(value))); \
        return this

#define FIELD_UINT_VEC(n, m) \
    case #n ##_h64: \
        m.push_back(static_cast<decltype(m)::value_type>(std::stoull(value))); \
        return this

#define FIELD_FLOAT_VEC(n, m) \
    case #n ##_h64: \
        m.push_back(std::stof(value)); \
        return this

#define FIELD_DOUBLE_VEC(n, m) \
    case #n ##_h64: \
        m.push_back(std::stod(value)); \
        return this

#define FIELD_STR_VEC(n, m) \
    case #n ##_h64: \
        m.push_back(value); \
        return this

#define FIELD_MEM(n, m) \
    case #n ##_h64: \
        return &m;

#define FIELD_MEMP(n, m) \
    case #n ##_h64: \
        return m;

#define FIELD_MEM_VEC(n, m) \
    case #n ##_h64: \
        m.resize(m.size() + 1); \
        return &m.back();

#define FIELD_MEMP_VEC(n, m) \
    case #n ##_h64: \
        m.resize(m.size() + 1); \
        return m.back();

#define FIELD_MEMS_VEC(n, m, T) \
    case #n ##_h64: { \
        auto s = std::make_shared<T>(); \
        m.push_back(s); \
        return s.get(); }
